<template>
  <view class="tv-view" :class="[`tv-view${_uid}`, isActive ? 'active' : '']">
    <slot></slot>
  </view>
</template>

<script>
  /**
   * tv-view 视图容器
   * @description 类似于view，用于包裹获取焦点的元素，需要在tv-page内使用
   * @property { preventDefault } 阻止默认事件
   * @event { tv-click } 按下确认键触发
   * @event { keydown } 阻止默认事件或未内置处理事件时触发，返回按键类型
   */
  import { getBestCandidateRect } from '../../util/focus.js';
  import mixin from '../../mixin/mixin.js';

  export default {
    mixins: [mixin],
    props: {
      // 阻止默认事件
      preventDefault: {
        type: Boolean,
        default: false
      }
    },
    inject: ['tvProps', 'areaUid'],
    data: () => ({
      tvState: {}
    }),
    computed: {
      isActive() {
        return this.areaUid === this.tvProps.tvFocus[this.tvProps.areaIndex].uid && this.tvState.tvViews && this.tvState.tvViews[this.tvState.index] && this.tvState.tvViews[this.tvState.index]._uid === this._uid;
      }
    },
    watch: {
      isActive: {
        immediate: true,
        handler(val) {
          val && this.tvState.triggerClick && this.$emit('tv-click');
        }
      }
    },
    methods: {
      // 点击事件、按键事件
      viewKeyClick(type) {
        // 阻止默认事件
        if (this.tvProps.preventDefault || this.preventDefault) { this.$emit('keydown', type); return; }
        // 按键处理
        const focusMoveMap = {
          enter: () => {
            return this.$emit('tv-click');
          },
          focusMove: (type) => {
            const { tvViews, index } = this.tvState;
            const { rect } = tvViews[index];
            const betterCandidate = getBestCandidateRect(rect, tvViews, type);
            !betterCandidate && focusMoveMap.changeArea(type);
            betterCandidate && (this.tvState.index = tvViews.findIndex(v => v._uid === betterCandidate.uid));
          },
          changeArea: (type) => {
            const { tvFocus, areaIndex } = this.tvProps;
            const { rect } = tvFocus[areaIndex];
            const betterCandidate = getBestCandidateRect(rect, tvFocus, type);
            betterCandidate && this.tvProps.tvFocus[this.tvProps.tvFocus.findIndex(v => v.uid === betterCandidate.uid)].tvViews.length && (this.tvProps.areaIndex = this.tvProps.tvFocus.findIndex(v => v.uid === betterCandidate.uid));
          }
        };
        ['up', 'down', 'left', 'right'].includes(type) && focusMoveMap.focusMove(type);
        focusMoveMap[type] && focusMoveMap[type]();
        this.$emit('keydown', type);
      },
      // 更新元素布局信息
      async updateRect() {
        const rect = await this.$getRect(`.tv-view${this._uid}`);
        this.tvState.tvViews[this.tvState.tvViews.findIndex(v => v._uid === this._uid)].rect = Object.assign({}, rect, { uid: this._uid });
      }
    },
    async mounted() {
      const rect = await this.$getRect(`.tv-view${this._uid}`);
      this.tvState = this.tvProps.tvFocus.find(k => k.uid === this.areaUid);
      this.tvState.tvViews.push(Object.assign({}, this, { rect: Object.assign({}, rect, { uid: this._uid }) }));
    },
    destroyed() {
      this.tvState.tvViews.splice(this.tvState.tvViews.findIndex(v => v._uid === this._uid), 1);
    }
  };
</script>

<style lang="scss" scoped>
.tv-view {
  &.active {
    box-shadow: rgb(67, 149, 254) 0 0 5rpx inset;
  }
}
</style>
